package howl.model;

import java.util.List;

import howl.types.Alarm;
import howl.types.DataProvider;
import howl.types.Datum;
import howl.types.Entry;
import howl.types.HowlUser;
import howl.types.Report;
import howl.types.TableDescriptor;
import howl.types.query.QueryHelper;
import howl.types.query.ResultSet;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.annotations.Persistent;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.users.User;
import compiler.exceptions.HowlModelException;
import compiler.exceptions.HowlUnauthorisedAction;
import compiler.terminals.expression.Node;

public class Model {

	private PersistenceManager pm;

	private HowlUser hu;

	public Model(HowlUser hu) {
		super();
		this.pm = howl.model.PMF.get().getPersistenceManager();
		this.hu = hu;
	}

	public Model() {
		super();
		this.pm = howl.model.PMF.get().getPersistenceManager();
		this.hu = null;
	}

	public void addTableDescriptor(TableDescriptor td)
			throws HowlModelException {
		validatePM();
		td
				.setName(td.getName() + "@"
						+ this.hu.getEmailAddress().split("@")[0]);
		if (this.getTableDescriptor(td.getName()) == null) {
			this.pm.makePersistent(td);
			FeedAuthentication.addAccess(td, hu);
		} else {
			throw new HowlModelException(td.getName()
					+ " already exists in the datastore");
		}
	}

	public TableDescriptor getTableDescriptor(String name)
			throws HowlModelException {
		validatePM();
		Query query = pm.newQuery("select from howl.types.TableDescriptor "
				+ "where Name == Param " + "parameters String Param "
				+ "import howl.types.TableDescriptor;");

		List<TableDescriptor> results = (List<TableDescriptor>) query
				.execute(name);

		if (results.size() > 0) {
			return results.get(0);
		} else {
			return null;
		}
	}

	public TableDescriptor getTableDescriptor(Long id)
			throws HowlModelException {
		validatePM();
		Query query = pm.newQuery("select from howl.types.TableDescriptor "
				+ "where id == Param " + "parameters Long Param "
				+ "import howl.types.TableDescriptor;");

		List<TableDescriptor> results = (List<TableDescriptor>) query
				.execute(id);

		if (results.size() > 0) {
			return results.get(0);
		} else {
			return null;
		}
	}

	public void deleteTableDescriptor(TableDescriptor td)
			throws HowlModelException {
		validatePM();
		TableDescriptor t = td;
		if (t != null) {
			if (FeedAuthentication.hasAccess(t, this.hu)) {
				Query query = pm
						.newQuery("select from howl.types.DataProvider where FeedOwner == Param1 parameters "
								+ "Long Param1 import howl.types.TableDescriptor;");

				List<DataProvider> dps = (List<DataProvider>) query.execute(td
						.getId());

				for (DataProvider p : dps) {
					this.removeDataProvider(td, p.getName());
				}
				FeedAuthentication.removeAllAccess(td);
				pm.deletePersistent(td);
			} else {
				throw new HowlModelException("The User "
						+ this.hu.getUsers().getNickname()
						+ " does not have access to " + td.getName());
			}

		} else {
			throw new HowlModelException(td.getName()
					+ "does not exist in the datastore");
		}

	}

	public void addDataProvider(TableDescriptor td, DataProvider s)
			throws HowlModelException {
		validatePM();
		Query query = pm
				.newQuery("select from howl.types.DataProvider where Name == Param0 && FeedOwner == Param1 parameters "
						+ "String Param0, Long Param1 import howl.types.TableDescriptor;");

		List<DataProvider> t = (List<DataProvider>) query.execute(s.getName(),
				td.getId());
		if (t.size() == 0) {
			if (FeedAuthentication.hasAccess(td, this.hu)) {
				s.setFeedOwner(td.getId());
				pm.makePersistent(s);
			} else {
				throw new HowlModelException("The User "
						+ this.hu.getUsers().getNickname()
						+ " does not have access to " + td.getName());
			}
		} else {
			throw new HowlModelException("A dataprovider called " + s.getName()
					+ " already exists for this feed in the datastore");
		}

	}

	public List<DataProvider> getDataProvider(TableDescriptor td)
			throws HowlModelException {
		validatePM();
		Query query = pm
				.newQuery("select from howl.types.DataProvider where FeedOwner == Param1 parameters "
						+ "Long Param1 import howl.types.TableDescriptor;");

		List<DataProvider> t = (List<DataProvider>) query.execute(td.getId());

		return t;

	}

	public DataProvider getDataProvider(TableDescriptor td, String name)
			throws HowlModelException {
		validatePM();
		Query query = pm
				.newQuery("select from howl.types.DataProvider where FeedOwner == Param1 && Name == Param2 parameters "
						+ "Long Param1, String Param2 import howl.types.TableDescriptor;");

		List<DataProvider> t = (List<DataProvider>) query.execute(td.getId(),
				name);

		if (t.size() == 0) {
			return null;
		} else {
			return t.get(0);
		}
	}

	public void removeDataProvider(TableDescriptor td, String name) {
		validatePM();

		if (FeedAuthentication.hasAccess(td, this.hu)) {

			Query datums = this.pm
					.newQuery("select from howl.types.Datum where GrandParentID == Param "
							+ "parameters Long Param "
							+ "import howl.types.Datum");

			Query enteries = this.pm
					.newQuery("select from howl.types.Entry where SensorName == Param "
							+ "parameters String Param "
							+ "import howl.types.Entry");

			// List<Datum> d = (List<Datum>) datums.execute(td.getId());

			datums.deletePersistentAll(td.getId());

			/*
			 * while (d.size() > 0) { this.pm.deletePersistentAll(d);
			 * 
			 * d = (List<Datum>) datums.execute(td.getId()); }
			 */

			// List<Entry> e = (List<Entry>) enteries.execute(name);

			enteries.deletePersistentAll(name);

			/*
			 * while (e.size() > 0) { this.pm.deletePersistentAll(e);
			 * 
			 * e = (List<Entry>) enteries.execute(name); }
			 */

			Query dp = this.pm
					.newQuery("select from howl.types.DataProvider where FeedOwner == Param0 && Name == Param1"
							+ " parameters Long Param0, String Param1 "
							+ "import howl.types.Datum");

			List<DataProvider> sen = (List<DataProvider>) dp.execute(
					td.getId(), name);
			pm.deletePersistent(sen.get(0));
		}
	}

	public void addAlarm(Alarm a) throws HowlUnauthorisedAction,
			HowlModelException {
		validatePM();
		if (FeedAuthentication.hasAccess(this.getTableDescriptor(a
				.getFeedname()), this.hu)) {
			a.setOwner(this.hu.getUsers());
			pm.makePersistent(a);
		} else {
			throw new HowlUnauthorisedAction("The User "
					+ this.hu.getUsers().getNickname()
					+ " does not have access to " + a.getFeedname()
					+ " and can not add alarms to it");
		}
	}

	public void removeAlarm(HowlUser hu) {
		validatePM();
		Query query = pm.newQuery("select from howl.types.Alarm where "
				+ "owner == Param parameters User Param "
				+ "import howl.types.Alarm; "
				+ "import com.google.appengine.api.users.User;");

		query.deletePersistentAll(hu.getUsers());
		/*
		 * List<Alarm> as = (List<Alarm>) query.execute(hu.getUsers());
		 * 
		 * pm.deletePersistentAll(as);
		 */
	}

	public void addReport(Report r) {
		validatePM();
		r.setOwner(this.hu.getUsers());
		pm.makePersistent(r);
	}

	public void removeReport(HowlUser hu) {
		validatePM();
		Query query = pm.newQuery("select from howl.types.Report where "
				+ "owner == Param parameters User Param "
				+ "import howl.types.Report; "
				+ "import com.google.appengine.api.users.User;");

		query.deletePersistentAll(hu.getUsers());
	}

	public void insertData(TableDescriptor td, Object... objs) {
		validatePM();
		if (FeedAuthentication.hasAccess(td, hu)) {
			pm.makePersistentAll(objs);
		}
	}

	static void addUser(HowlUser hu) {
		PMF.get().getPersistenceManager().makePersistent(hu);
	}

	public static Model AddUser(HowlUser hu) throws HowlModelException {
		PersistenceManager pm = howl.model.PMF.get().getPersistenceManager();

		Query query = pm
				.newQuery("select from howl.types.HowlUser "
						+ "where  users == Param "
						+ "parameters User Param "
						+ "import howl.types.HowlUser; import com.google.appengine.api.users.User;");

		List<HowlUser> results = (List<HowlUser>) query.execute(hu.getUsers());
		if (results.size() == 0) {
			pm.makePersistent(hu);
			return new Model(hu);
		} else {
			throw new HowlModelException(hu.getUsers().getEmail()
					+ " already has a registered account");
		}
	}

	public static void removeUser(HowlUser hu) throws HowlModelException {
		PersistenceManager pm = howl.model.PMF.get().getPersistenceManager();
		Query query = pm.newQuery("select from howl.types.HowlUser "
				+ "where  id == Param " + "parameters Long Param "
				+ "import howl.types.HowlUser;");

		List<HowlUser> results = (List<HowlUser>) query.execute(hu.getId());
		if (results.size() > 0) {
			pm.deletePersistent(results.get(0));
		} else {
			throw new HowlModelException(hu.getEmailAddress()
					+ " has no account in the datastore");
		}
	}

	public List<TableDescriptor> getallCurrentUsersFeeds() {
		validatePM();
		return FeedAuthentication.getAvailable(hu);
	}

	/**
	 * Used to populate app engine generated ID fields
	 * 
	 * @param j
	 */
	void registerObject(Object j) {
		validatePM();
		this.pm.makePersistent(j);
		this.pm.refreshAll();
	}

	public boolean hasAccess(TableDescriptor td) {
		validatePM();
		return FeedAuthentication.hasAccess(td, this.hu);
	}

	public void addAccess(TableDescriptor td, HowlUser hu) {
		if (FeedAuthentication.hasAccess(td, this.hu)) {
			FeedAuthentication.addAccess(td, hu);
		}
	}

	public ResultSet query(String Name, Node exp) throws HowlModelException {
		validatePM();
		if (this.hasAccess(this.getTableDescriptor(Name))) {
			return QueryHelper.query(exp, Name);
		} else {
			throw new HowlModelException("The User "
					+ this.hu.getUsers().getNickname()
					+ " does not have access to " + Name);
		}
	}

	public ResultSet query(String Name, int max) throws HowlModelException {
		validatePM();
		if (this.hasAccess(this.getTableDescriptor(Name))) {
			return QueryHelper.queryByEntity(Name, max);
		} else {
			throw new HowlModelException("The User "
					+ this.hu.getUsers().getNickname()
					+ " does not have access to " + Name);
		}
	}

	public static HowlUser getHowlUserfromUser(User u) {

		PersistenceManager pm = PMF.get().getPersistenceManager();

		Query query = pm
				.newQuery("select from howl.types.HowlUser "
						+ "where users == Param "
						+ "parameters User Param "
						+ "import howl.types.HowlUser;import com.google.appengine.api.users.User;");
		List<HowlUser> results = (List<HowlUser>) query.execute(u);

		if (results.size() < 1) {
			return null;
		} else {
			return results.get(0);
		}
	}

	public static HowlUser getHowlUserfromEmail(String email) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery("select from howl.types.HowlUser "
				+ "where emailAddress == Param " + "parameters String Param "
				+ "import howl.types.HowlUser;");

		List<HowlUser> results = (List<HowlUser>) query.execute(email);

		if (results.size() > 0) {
			return results.get(0);
		} else {
			return null;
		}
	}

	public static HowlUser getHowlUserfromTwitterName(String tname) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery("select from howl.types.HowlUser "
				+ "where twitterName == Param " + "parameters String Param "
				+ "import howl.types.HowlUser");

		List<HowlUser> results = (List<HowlUser>) query.execute(tname);

		if (results.size() > 0) {
			return results.get(0);
		} else {
			return null;
		}
	}

	public HowlUser getHowlUser() {
		validatePM();
		Query query = pm
				.newQuery("select from howl.types.HowlUser "
						+ "where users == Param "
						+ "parameters User Param "
						+ "import howl.types.HowlUser;import com.google.appengine.api.users.User;");
		List<HowlUser> results = (List<HowlUser>) query.execute(this.hu
				.getUsers());

		if (results.size() < 1) {
			return null;
		} else {
			return results.get(0);
		}
	}

	public HowlUser getAuthenticatedUser() {
		return this.hu;
	}

	/**
	 * Used to get a refernece to the Persistent managed used by the Model
	 * 
	 * @return the pm
	 */
	public PersistenceManager getPm() {
		validatePM();
		return pm;
	}

	private void validatePM() {
		if (this.pm.isClosed()) {
			this.pm = PMF.get().getPersistenceManager();
		}
	}
}
